package org.texteasy;

import java.util.ArrayList;
import java.util.HashSet;

import org.texteasy.BulkMessenger.GroupListItem;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Contacts;
import android.provider.Contacts.Groups;
import android.provider.Contacts.People;
import android.provider.Contacts.People.Phones;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;

public class ContactsAccessorSdk3_4 extends ContactsAccessor {

	//Use type as the unique phone number id as only one number per cotact (primary) will be show. 
	//private String[] contactsProjection = new String[] {People._ID,People.NAME, People.TYPE,People.NUMBER, People.TYPE};
	private String[] contactsProjection = new String[] {android.provider.Contacts.Phones.PERSON_ID, android.provider.Contacts.Phones.NAME, 
			android.provider.Contacts.Phones.TYPE,android.provider.Contacts.Phones.NUMBER, 
			android.provider.Contacts.Phones._ID};
	private String[] groupsProjection = new String[]{Groups._ID, Groups.SYSTEM_ID, Groups.NAME};
	
	@Override
	public Cursor queryContacts(ContentResolver contentResolver, String filter, long selectedGroupId,
			ArrayList<Integer> phoneTypes, boolean primaryNumbersOnly) {
		
		if(filter == null || "".equals(filter.trim())){
			filter = "%";
		}else{
			filter = filter + "%";
		}
		
		StringBuilder whereClause = new StringBuilder(android.provider.Contacts.Phones.NUMBER).
		append(" is not null and ").
		append(android.provider.Contacts.Phones.NAME).
		append(" like '").
		append(filter).
		append("'");
		
		filterPhoneTypes(phoneTypes, whereClause);
		
		if(primaryNumbersOnly){
			whereClause.append(" AND phones.").
				append(android.provider.Contacts.Phones._ID).
				append(" in (").
				append(generatePrimaryPhoneWhereCaluse(contentResolver)).
				append(")");
		}
		
		if(selectedGroupId >=0){
			
			whereClause.append(" AND ").
				append(android.provider.Contacts.Phones.PERSON_ID).
				append(" in ").
				append("(SELECT person FROM groups, groupmembership WHERE ((groups._id=groupmembership.group_id OR " +
				"(groups._sync_account=groupmembership.group_sync_account AND groups._sync_id=groupmembership.group_sync_id))) " +
				"AND (GROUPS._ID = ").
				append(selectedGroupId).
				append("))");
		}
		
		return contentResolver.query(android.provider.Contacts.Phones.CONTENT_URI, contactsProjection, 
			whereClause.toString(), null, android.provider.Contacts.Phones.NAME);
	}

	private String generatePrimaryPhoneWhereCaluse(ContentResolver contentResolver){
		
		//Can't know exactly what a query needs to look like as each version
		//of android can change it, but we do know what is supported so write a 
		//contentResolver.query but put an invalid token in to cause an exception
		//then parse the query out. 
		//Note this is a hack subject to break if the query isn't printed in the 
		//exception
		
		String toReturn = null;
		try{
			contentResolver.query(People.CONTENT_URI, new String[]{People.PRIMARY_PHONE_ID}, 
				null, null, "INVALID");//the INVALID token will be stripped from the sql in the exception
		}catch(Exception ex){
			
			String exception = ex.getMessage();
			int start = exception.toUpperCase().indexOf("SELECT ");
			int end = exception.toUpperCase().indexOf("ORDER BY INVALID");
			toReturn = exception.substring(start, end);
		}
		
		return toReturn;
	}
	
	private void filterPhoneTypes(ArrayList<Integer> phoneTypes, StringBuilder whereClause) {
		//get the phoneTypes to filter
		//There are a lot more Phone types than we show on the filter
		//The ones not shows are going to be associated with PhoneTypeOther
		//When making the query if PhoneTypeOther is selected then include
		//all PhoneTypes except those unselected in the Preferences (i.e.
		//if Other is selected and all the rest in prefs are unselected then
		//put in where clause: where Phone.Type not in (PhoneType.Mobile, PhoneType.Home...)
		//If Other and Mobile is selected and the rest are unselected then
		//where becomes: where Phone.Type not in (PhoneType.Home...))
		//If Other is not selected then where clause becomes a simple "in" affair where
		//all selected types are in the "in" statement
		//(i.e. where Phone.Type in (PhoneType.Mobile, PhoneType.Home...) )
		//If all types are selected then don't add anything to the where clause
		boolean[] selected = new boolean[]{false,false,false,false,false};
		for(int phoneType: phoneTypes){
			if(phoneType == Phones.TYPE_OTHER){
				selected[0] = true;
			}
			if(phoneType == Phones.TYPE_MOBILE){
				selected[1] = true;
			}
			if(phoneType == Phones.TYPE_CUSTOM){
				selected[2] = true;
			}
			if(phoneType == Phones.TYPE_HOME){
				selected[3] = true;
			}
			if(phoneType == Phones.TYPE_WORK){
				selected[4] = true;
			}
		}
		
		boolean allSelected = true;
		for(boolean selectedType: selected){
			if(!selectedType){
				allSelected = false;
			}
		}
		
		if(allSelected){
			//All are selected
		}else if(selected[0]){
			
			//Other is selected
			whereClause.append(" AND ").
				append(People.TYPE).
				append(" NOT IN (").
				append(buildInStatement(selected, true)).
				append(")");
		}else{
			//Other is not selected
			whereClause.append(" AND ").
			append(People.TYPE).
			append(" IN (").
			append(buildInStatement(selected, false)).
			append(")");
		}
	}

	private String buildInStatement(final boolean[] origSelected, 
		final boolean reverseSelections) {
		
		boolean[] selected = origSelected;
		if(reverseSelections){
			for(int i = 0; i < selected.length ;i++){
				selected[i] = !selected[i];
			}
		}
		
		StringBuilder toReturn = new StringBuilder();
		if(selected[1]){
			toReturn.append(Phones.TYPE_MOBILE).
				append(",");
		}
		
		if(selected[2]){
			toReturn.append(Phones.TYPE_CUSTOM).
				append(",");
		}
		
		if(selected[3]){
			toReturn.append(Phones.TYPE_HOME).
				append(",");
		}
		
		if(selected[4]){
			toReturn.append(Phones.TYPE_WORK).
				append(",");
		}
		
		if(toReturn.length() == 0){
			return "";
		}else{
			return toReturn.substring(0, toReturn.length() -1 );
		}
	}

	@Override
	public ArrayList<GroupListItem> queryGroups(Context context,ContentResolver contentResolver) {
		
		ArrayList<GroupListItem> groups = new ArrayList<GroupListItem>();
		groups.add(new GroupListItem(-1l, context.getString(R.string.ALL_CONTACTS_WITH_PHONES), 
				null, null));
		
		Cursor cursor =  contentResolver.query(Groups.CONTENT_URI, 
			groupsProjection, null, null, Contacts.Groups.DEFAULT_SORT_ORDER);
		
		while (cursor.moveToNext()) {
			String systemId = cursor.getString(BulkMessenger.GROUPS_COLUMN_INDEX_SYSTEM_ID);
			String name = cursor.getString(BulkMessenger.GROUPS_COLUMN_INDEX_NAME);
			Long groupId = cursor.getLong(BulkMessenger.GROUPS_COLUMN_INDEX_ID);
			
			if (cursor.isNull(BulkMessenger.GROUPS_COLUMN_INDEX_SYSTEM_ID)
					&& !Groups.GROUP_MY_CONTACTS.equals(systemId)) {
				groups.add(new GroupListItem(groupId, name, null, null));
			} else {
				// found the "My Contacts" this displays weird (System Group: My
				// Contacts)
				// so gave it a different name
				groups.add(new GroupListItem(groupId, context.getString(
						R.string.GROUP_NAME_MY_CONTACTS),null, null));
			}
		}
		
		return groups;
	}

	
	@Override
	public Cursor queryContactsForPhoneNumbers(ContentResolver contentResolver,
			HashSet<Long> selectedIds) {
		
		StringBuffer buffer = new StringBuffer(" in(");
    	for(Long selectedId: selectedIds){
    		buffer.append(selectedId).
				append(","); 
    	}
		
    	String inStatement = buffer.substring(0, buffer.length() - 1) + ")";
		
        String[] projection = new String[]{android.provider.Contacts.Phones.PERSON_ID, 
        	android.provider.Contacts.Phones.NUMBER, 
        	android.provider.Contacts.Phones._ID};
        
        Uri contacts = android.provider.Contacts.Phones.CONTENT_URI;
        
        return contentResolver.query(contacts, projection, 
    		android.provider.Contacts.Phones.PERSON_ID + inStatement, 
    		null, null);
	}
	
	@Override
	public String getPhoneColumnIndex() {
		return People.NUMBER;
	}
}
